package com.YKFuse.bean.operation.pool;

import com.YKFuse.bean.operation.alert.YKFuseAlertService;
import com.YKFuse.wrapper.MethodDelayWrapper;
import com.autoconfig.YKFuseProperties;
import lombok.Data;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.concurrent.*;

/**
 * @author yuank
 * @version 1.0.0
 * @date 2025/2/11 15:04:52
 */
@Data
public class YKFusePoolOperationImpl implements YKFusePoolOperation, ApplicationContextAware {

    private YKFuseProperties properties;

    //private YKFuseAlertService ykFuseAlertService;  阅读YkFuseAutoConfiguration有说明 交给代码处处理这个bean了
    /**
     * 处于熔断池状态的方法
     */
    private HashSet<Method> methodSet;
    /**
     * 延时队列用于熔断重试支持
     */
    private DelayQueue<MethodDelayWrapper> delayQueue;
    /**
     * 执行器
     */
    private ExecutorService threadPoolExecutor;
    /**
     * 用于获取告警bean
     */
    private ApplicationContext applicationContext;

    public YKFusePoolOperationImpl(YKFuseProperties properties) {
        this.properties = properties;
        this.methodSet = new HashSet<>();
        this.delayQueue = new DelayQueue<>();
        this.threadPoolExecutor = new ThreadPoolExecutor(1, 5, 60,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
    }

    //用于 启动线程池
    //因为在构造器启动 我们需要在run方法中使用applicationContext动态选择alertBean 而applicationContext需要全部bean初始化完毕 于是会出现类似"死锁"的问题
    @PostConstruct
    public void init() {
        final Thread thread = new Thread(() -> {

            // 对于先执行还是先判断判断次数 由于我们告警的需求是需要返回值的 所以是优先执行再判断
            // 把重试次数校验判断逻辑 从 >= 改 > 即可
            while (true) {
                try {
                    MethodDelayWrapper methodWrapper = delayQueue.take();
                    if (methodWrapper != null) {
                        threadPoolExecutor.execute(() -> {
                            try {
                                //重试
                                methodWrapper.invoke();

                            //成功了取出set标识
                                delayQueue.remove(methodWrapper);

                            //失败
                            } catch (Exception e) {
                                //增加重试次数
                                methodWrapper.haveTry();

                                //重试次数 > maxRetry 触发告警
                                if (methodWrapper.getHaveTryTimes() > properties.getMaxRetry()) {
                                    // 支持 功能自定义告警
                                    final YKFuseAlertService alertService = applicationContext.getBean(properties.getAlertClassName(), YKFuseAlertService.class);
                                    if (alertService != null) {
                                        alertService.alert(methodWrapper.getBean(), methodWrapper.getMethod(), methodWrapper.getParams(), e);
                                    } else {
                                        YKFuseAlertService defaultAlertServiceImpl = applicationContext.getBean("defaultAlertServiceImpl", YKFuseAlertService.class);
                                        defaultAlertServiceImpl.alert(methodWrapper.getBean(), methodWrapper.getMethod(), methodWrapper.getParams(), e);
                                    }

                                //再次进入重试队列
                                } else {
                                    //设置
                                    methodWrapper.setExpireTime(System.currentTimeMillis() + properties.getRetryInterval());
                                    delayQueue.add(methodWrapper);
                                }
                            }
                        });
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                try {
                    Thread.sleep(100L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        //守护化
        thread.setDaemon(true);
        thread.start();
    }



    @Override
    public boolean isInPoor(Method method) {
        return methodSet.contains(method);
    }

    @Override
    public void trigger(MethodDelayWrapper methodDelayWrapper) {
        //记录入池
        methodSet.add(methodDelayWrapper.getMethod());
        //入池操作
        delayQueue.offer(methodDelayWrapper);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}